﻿using Essensoft.Paylink.Alipay;
using Essensoft.Paylink.Alipay.Domain;
using Essensoft.Paylink.Alipay.Notify;
using Essensoft.Paylink.Alipay.Request;
using Essensoft.Paylink.WeChatPay;
using Essensoft.Paylink.WeChatPay.V3;
using Essensoft.Paylink.WeChatPay.V3.Domain;
using Essensoft.Paylink.WeChatPay.V3.Notify;
using Essensoft.Paylink.WeChatPay.V3.Request;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using StudyCompanion.Commons.Config;
using StudyCompanion.Commons.Result;
using StudyCompanion.Core.Enum;
using StudyCompanion.Core.Repository;
using StudyCompanion.Core.Services;
using StudyCompanion.Dto.Order;
using StudyCompanion.Entity.Order;
using StudyCompanion.Entity.System;
using StudyCompanion.Entity.User;
using StudyCompanion.IServices;
using StudyCompanion.Model.Order;

namespace StudyCompanion.Services;

public class OrderService : BaseService<Order>, IOrderService
{
    /// <summary>
    /// </summary>
    private readonly IAlipayClient _alipayClient;

    /// <summary>
    /// </summary>
    private readonly IOptions<AlipayOptions> _alipayOptions;

    private readonly ILogger<OrderService> _logger;

    /// <summary>
    /// </summary>
    private readonly IOptions<NoticeOptions> _noticeOption;

    /// <summary>
    /// </summary>
    private readonly IWeChatPayClient _weChatPayClient;

    /// <summary>
    /// </summary>
    private readonly IOptions<WeChatPayOptions> _weChatPayOptions;

    private readonly IEventHandleAppService _eventHandle;
    private readonly IWalletLogService _walletLogService;


    public OrderService(BaseRepository<Order> repository,
        IWeChatPayClient weChatPayClient,
        IOptions<WeChatPayOptions> weChatPayOptions,
        IAlipayClient alipayClient,
        IOptions<AlipayOptions> alipayOptions,
        IOptions<NoticeOptions> noticeOption,
        ILogger<OrderService> logger,
        IEventHandleAppService eventHandle,
        IWalletLogService walletLogService) : base(repository)
    {
        _weChatPayClient = weChatPayClient;
        _weChatPayOptions = weChatPayOptions;
        _alipayClient = alipayClient;
        _alipayOptions = alipayOptions;
        _noticeOption = noticeOption;
        _logger = logger;
        _eventHandle = eventHandle;
        _walletLogService = walletLogService;
    }

    /// <summary>
    ///     创建订单
    /// </summary>
    /// <param name="userId"></param>
    /// <param name="cardId"></param>
    /// <returns></returns>
    public async Task<Tuple<long?, string>> Create(long userId, long cardId)
    {
        var user = _repository.Queryable<Users>().FirstAsync(x => x.Id == userId);
        if (user == null)
            return new Tuple<long?, string>(null, "用户信息错误");
        var card = await _repository.Queryable<ChargeCard>().FirstAsync(x => x.Id == cardId);
        if (card == null)
            return new Tuple<long?, string>(null, "参数错误");

        //商品总价
        var orderAmount = card.Price;
        //优惠金额
        var discountAmount = 0m;
        //支付金额
        var payAmount = orderAmount - discountAmount;

        var order = new Order
        {
            UserId = userId,
            Code = CreateOrderCode(),
            OrderAmount = orderAmount,
            DiscountAmount = discountAmount,
            PaidAmount = payAmount,
            Months = card.Months,
            Rebate = card.Rebate,
            Status = (int)OrderStatusEnum.Unpaid
        };
        var orderId = await _repository.InsertReturnBigIdentityAsync(order);
        return new Tuple<long?, string>(orderId, string.Empty);
    }

    /// <summary>
    ///     添加订单并支付
    /// </summary>
    /// <param name="input"></param>
    /// <param name="userId"></param>
    /// <returns></returns>
    public async Task<CommonResult<AddOrderVo>> AddOrderAndPay(OrderAddDto input, long userId)
    {
        var user = await _repository.Queryable<Users>().FirstAsync(x => x.Id.Equals(userId));
        if (user == null)
            return Problem("用户不存在");
        var card = _repository.Queryable<ChargeCard>().First(x => x.Id == input.GoodsId);
        if (card == null)
            return Problem("充值卡不存在");
        var orderInfo = await _repository.Where(x =>
                x.UserId == userId && x.Status == (int)OrderStatusEnum.Unpaid &&
                x.CreateTime >= DateTime.Now.AddHours(-1))
            .FirstAsync();
        if (orderInfo != null)
            return new AddOrderVo
            {
                PayMethod = orderInfo.PayType switch
                {
                    1 => PayMethodEnum.WechatPay,
                    2 => PayMethodEnum.WechatPay,
                    _ => PayMethodEnum.Balance
                },
                Result = orderInfo.CodeUrl,
                OrderId = orderInfo.Id
            };
        
        orderInfo = new Order
        {
            UserId = userId,
            Code = CreateOrderCode(),
            OrderAmount = card.Price,
            Months = card.Months,
            PaidAmount = card.Price,
            Rebate = card.Rebate,
            DiscountAmount = 0,
            Status = (int)OrderStatusEnum.Unpaid,
            PayType = (int)input.PayMethod,
            TradeState = (int)TradeStateEnum.Unpaid
        };
        orderInfo.Id = await _repository.InsertReturnBigIdentityAsync(orderInfo);
        if (input.PayMethod == PayMethodEnum.Balance)
        {
            if (user.WalletBalance <= orderInfo.PaidAmount)
                return Problem("钱包余额不足");
            try
            {
                _walletLogService.WriteWalletLog(user.Id, WalletActionEnum.Pay, WalletTypeEnum.Reduce, orderInfo.PaidAmount,
                    orderInfo.Id, "购买月卡", null);
                var validTime = user.ValidTime <= DateTime.Now
                    ? DateTime.Now.AddMonths(orderInfo.Months)
                    : user.ValidTime.AddMonths(orderInfo.Months);
                await _eventHandle.RebateAndUpgrade(orderInfo.Id, null);
                await _repository.Change<Users>().AsUpdateable().Where(x => x.Id == orderInfo.Id)
                    .SetColumns(x => x.ValidTime == validTime)
                    .ExecuteCommandAsync();
                return new AddOrderVo { PayMethod = PayMethodEnum.Balance, Result = "ok", OrderId = orderInfo.Id };
            }
            catch
            {
                return Problem("余额支付失败");
            }
            finally
            {
                _repository.RollbackTran();
            }
        }

        switch (input.PayMethod)
        {
            case PayMethodEnum.WechatPay:
            {
                var result = await WaChatPay(orderInfo.Code, orderInfo.PaidAmount);
                if (!result.IsSuccese)
                {
                    _logger.Log(LogLevel.Information, $"微信支付失败:{result.Message}", null);
                    return Problem("微信支付失败");
                }

                await _repository.AsUpdateable().Where(x => x.Id == orderInfo.Id)
                    .SetColumns(x => x.CodeUrl == result.Data)
                    .ExecuteCommandAsync();
                return new AddOrderVo { PayMethod = PayMethodEnum.WechatPay, Result = result.Data, OrderId = orderInfo.Id };
            }
            case PayMethodEnum.AliPay:
            {
                var result = await AliPay(orderInfo.Code, orderInfo.PaidAmount);
                if (!result.IsSuccese)
                {
                    _logger.Log(LogLevel.Information, $"支付宝支付失败:{result.Message}", null);
                    return Problem("支付宝支付失败");
                }

                await _repository.AsUpdateable().Where(x => x.Id == orderInfo.Id)
                    .SetColumns(x => x.CodeUrl == result.Data)
                    .ExecuteCommandAsync();
                return new AddOrderVo { PayMethod = PayMethodEnum.AliPay, Result = result.Data, OrderId = orderInfo.Id };
            }
            default:
                return Problem("参数错误");
        }
    }

    /// <summary>
    ///     获取订单支付状态
    /// </summary>
    /// <param name="orderId"></param>
    /// <param name="userId"></param>
    /// <returns></returns>
    public async Task<CommonResult<int>> GetOrderTradeState(long orderId, long userId)
    {
        var order = await _repository.GetFirstAsync(x => x.Id == orderId && x.UserId == userId);
        if (order == null)
            return Problem("参数错误");
        return order.TradeState;
    }

    /// <summary>
    ///     微信二维码扫码支付回调
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    public async Task WechatPayNotify(WeChatPayTransactionsNotify input)
    {
        var order = await _repository.GetFirstAsync(x => x.Code == input.OutTradeNo);
        if (order == null || order.Status != (int)OrderStatusEnum.Unpaid)
            return;
        switch (input.TradeState)
        {
            case WeChatPayTradeState.UserPaying: //发起支付
                order.TradeState = (int)TradeStateEnum.PAYING;
                _repository.Update(order);
                break;
            case WeChatPayTradeState.Success: //支付成功
                var user = await _repository.Queryable<Users>().FirstAsync(x => x.Id.Equals(order.UserId));
                user.ValidTime = user.ValidTime <= DateTime.Now
                    ? DateTime.Now.AddMonths(order.Months)
                    : user.ValidTime.AddMonths(order.Months);
                await _repository.Change<Users>().UpdateAsync(user);
                await _eventHandle.RebateAndUpgrade(order.Id, input.TransactionId);
                break;
            case WeChatPayTradeState.Closed:
                order.TradeState = (int)TradeStateEnum.CLOSED;
                _repository.Update(order);
                break;
        }
    }

    /// <summary>
    ///     支付宝扫码支付回调
    /// </summary>
    /// <param name="notify"></param>
    /// <returns></returns>
    public async Task AliPayNotify(AlipayTradePrecreateNotify notify)
    {
        var order = await _repository.GetFirstAsync(x => x.Code == notify.OutTradeNo);
        if (order == null || order.Status != (int)OrderStatusEnum.Unpaid)
            return;
        switch (notify.TradeStatus)
        {
            case AlipayTradeStatus.Wait: // 等待付款
                order.TradeState = (int)TradeStateEnum.PAYING;
                _repository.Update(order);
                break;
            case AlipayTradeStatus.Success: // 支付成功
                var user = await _repository.Queryable<Users>().FirstAsync(x => x.Id.Equals(order.UserId));
                user.ValidTime = user.ValidTime <= DateTime.Now
                    ? DateTime.Now.AddMonths(order.Months)
                    : user.ValidTime.AddMonths(order.Months);
                await _repository.Change<Users>().UpdateAsync(user);
                await _eventHandle.RebateAndUpgrade(order.Id, notify.TradeNo);
                break;
            case AlipayTradeStatus.Closed:
                order.Status = (int)OrderStatusEnum.CLOSED; //支付关闭
                _repository.Update(order);
                break;
        }
    }

    /// <summary>
    ///     发起微信支付（Native支付）
    /// </summary>
    /// <param name="orderCode"></param>
    /// <param name="amount"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    private async Task<CommonResult<string>> WaChatPay(string orderCode, decimal amount)
    {
        var model = new WeChatPayTransactionsNativeBodyModel
        {
            AppId = _weChatPayOptions.Value.AppId,
            MchId = _weChatPayOptions.Value.MchId,
            Amount = new Essensoft.Paylink.WeChatPay.V3.Domain.Amount
                { Total = Convert.ToInt32(amount * 100), Currency = "CNY" },
            Description = "购买会员",
            NotifyUrl = _noticeOption.Value.WeChatPay,
            OutTradeNo = orderCode
        };

        var request = new WeChatPayTransactionsNativeRequest();
        request.SetBodyModel(model);

        var response = await _weChatPayClient.ExecuteAsync(request, _weChatPayOptions.Value);
        if (!response.IsError)
            return response.CodeUrl;
        return Problem(response.Message);
    }

    /// <summary>
    ///     发起支付宝支付
    /// </summary>
    /// <returns></returns>
    private async Task<CommonResult<string>> AliPay(string orderCode, decimal amount)
    {
        var model = new AlipayTradePrecreateModel
        {
            OutTradeNo = orderCode,
            Subject = "购买会员",
            TotalAmount = amount.ToString(),
            Body = "购买会员"
        };
        var req = new AlipayTradePrecreateRequest();
        req.SetBizModel(model);
        req.SetNotifyUrl(_noticeOption.Value.AliPay);

        var response = await _alipayClient.CertificateExecuteAsync(req, _alipayOptions.Value);
        if (!response.IsError)
            return response.QrCode;
        return Problem(response.Msg);
    }

    /// <summary>
    /// 关闭微信支付
    /// </summary>
    /// <param name="orderCode"></param>
    /// <returns></returns>
    public async Task<CommonResult<string>> CloseWaChatPay(string orderCode)
    {
        var model = new WeChatPayTransactionsOutTradeNoCloseBodyModel
        {
            MchId = _weChatPayOptions.Value.MchId,
        };

        var request = new WeChatPayTransactionsOutTradeNoCloseRequest
        {
            OutTradeNo = orderCode,
        };

        request.SetBodyModel(model);

        var response = await _weChatPayClient.ExecuteAsync(request, _weChatPayOptions.Value);
        if (!response.IsError)
            Success("关闭成功");
        return Problem(response.Message);
    }

    private static string CreateOrderCode()
    {
        Random random = new();
        var strRandom = random.Next(10000, 99999).ToString();
        var code = $"D{DateTime.Now:yyyyMMddHHmmss}{strRandom}";
        return code;
    }
}